package com.zjl.redis.第19章_IO多路复用;

import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.*;
import java.util.concurrent.*;


/**
 * 当用户进程发出read操作时，如果kernel（内核）中的数据还没 有准备好，
 * 那么它并不会 block（等待） 用户进程，而是立刻返回一个error.
 * 从用户进程角度讲，它发起一个read操作后， 并不需要等待，而是马上就得到了一个结果。
 * 用户进程判断结果是一个error时， 它就知道数据还没有准备好，于是它可以再次发送read操作。
 * 一旦kernel中的数据准备好了，并且又再次收到了用户进程的system call,
 * 那么它马上就将数据拷贝到了用户内存，然后返回。
 * 所以，NIO特点是用户进程需要不断的主动询间内核数据准备好了吗? 一句话，用轮询替代阻塞!
 * 在NIO模式中，一切都是非阻塞的:
 * accept()方法是非阻塞的，如果没有客户端连接，就返回无连接标识
 * read()方法是非阻塞的，如果read()方法读取不到数据就返回空闲中标识，
 * 如果读取到数据时只阻塞read()方法读数据的时间
 * 在NIO模式中，只有一个线程:
 * 当一个客户端与服务端进行连接，这个socket就会加入到一个数组中，隔一段时间遍历一次,
 * 看这个socket的read()方法能否读到数据，这样一个线程就能处理多个客户端的连接和读取了
 *
 *  Java NIO 实际上就能实现多路复用I/O
 *  这里没用
 */
public class D_NIO {


    @Test
    public void 服务端() throws IOException{

        Selector selector = Selector.open();//得到选择器

        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();//获得ServerSocketChannel
        serverSocketChannel.socket().bind(new InetSocketAddress("127.0.0.1", 5438));//绑定IP 端口
        serverSocketChannel.configureBlocking(false);//设置为非阻塞IO    ture：阻塞IO
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);//注册到selector选择器中


        Set<SocketChannel> socketChannels = new HashSet<>();
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        while (true) {
            //遍历 所有连接     必须用迭代器，否则  删除时会抛出错误
            Iterator<SocketChannel> keys = socketChannels.iterator();
            while (keys.hasNext()){
                SocketChannel socketChannel = keys.next();
                try {

                    int read = socketChannel.read(byteBuffer);
                    if (read > 0) {
                        String array = new String(byteBuffer.array(),0,read);

                        //socketChannel.getRemoteAddress()     存储 连接者的IP和临时端口
                        System.out.println(socketChannel.getRemoteAddress() + "说：" + array);
                        byteBuffer.clear();
                    }
                }catch (IOException e){
                    //存储 连接者的IP和临时端口
                    System.out.println(socketChannel.getRemoteAddress() + "离线了");
//                    socketChannels.remove(socketChannel);// 此删除  删除时会抛出错误
                    keys.remove();//删除   防止重复操作
                }
            }

            SocketChannel socketChannel = serverSocketChannel.accept();
            if(socketChannel != null){
                //存储 连接者的IP和临时端口
                SocketAddress remoteAddress = socketChannel.getRemoteAddress();
                System.out.println(remoteAddress.toString() + "成功连接上");
                socketChannel.configureBlocking(false);//置为非阻塞IO    ture：阻塞IO
                socketChannels.add(socketChannel);
            }

        }
    }

    @Test
    public void 客户端() throws IOException {
        Socket socket = new Socket("127.0.0.1", 5438);

        Scanner sc = new Scanner(System.in);
        while (sc.hasNextLine()) {
            OutputStream outputStream = socket.getOutputStream();
            if(outputStream == null){
                break;
            }
            outputStream.write(sc.nextLine().getBytes());
        }

        socket.close();
    }


}
